package com.internetCafes.spms.common.module.mybatis.plugin;

import com.internetCafes.spms.common.CommonConstants;
import com.internetCafes.spms.common.utils.ShiroUtils;
import com.internetCafes.spms.core.annotation.NoTentant;
import com.internetCafes.spms.core.annotation.TentantFilter;
import com.sms.common.util.hutool.core.util.ObjectUtil;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;
import java.util.Properties;


/**
 * 共享数据库的多租户系统实现
 * Created by Meara on 2016/3/17.
 */
@Intercepts({
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
        @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class,RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class})
        //@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class MultiTenancyInterceptorPro implements Interceptor {

    private static final Logger logger= LoggerFactory.getLogger(MultiTenancyInterceptorPro.class);


    /**
     * 默认执行
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        final Object[] args = invocation.getArgs();
        final MappedStatement mappedStatement = (MappedStatement) args[0];
        final Object parameter = args[1];
        //logger.info("sql:{}",mappedStatement.getBoundSql(parameter).getSql());
        String namespace = mappedStatement.getId();
        String className = namespace.substring(0,namespace.lastIndexOf("."));
        String methodName= namespace.substring(namespace.lastIndexOf(".") + 1,namespace.length());
        Method[] ms = Class.forName(className).getMethods();
        for(Method m : ms){
            if(m.getName().equals(methodName)){
                if(m.getAnnotation(NoTentant.class) != null){
                    //NoTentant noTentant = m.getAnnotation(NoTentant.class);
                } else {
                    if(ShiroUtils.getUserId() == CommonConstants.SUPER_ADMIN){
                        break;
                    }
                    String tableAlias = null;
                    if (m.getAnnotation(TentantFilter.class) != null){
                        TentantFilter t = m.getAnnotation(TentantFilter.class);
                        tableAlias = t.tableAlias();
                    }

                    BoundSql boundSql = mappedStatement.getBoundSql(invocation.getArgs()[1]);
                    if (SqlParserUtils.isHaveEmptyList(boundSql.getSql())) {
                        return SqlParserUtils.getDefaultReturnValue(invocation);
                    }

                    StatementHandler statementHandler = (StatementHandler)invocation.getTarget();
                    //通过MetaObject优雅访问对象的属性，这里是访问statementHandler的属性
                    MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());
                    //先拦截到RoutingStatementHandler，里面有个StatementHandler类型的delegate变量，其实现类是BaseStatementHandler
                    // 然后获取到BaseStatementHandler的成员变量mappedStatement
                    //MappedStatement mappedStatement = (MappedStatement)metaObject.getValue("delegate.mappedStatement");
                    // 配置文件中SQL语句的ID
                    //String id = mappedStatement.getId();
                    //if(id.matches(".+ByPage$")) { //需要拦截的ID(正则匹配)
                        BoundSql _boundSql = statementHandler.getBoundSql();
                        String sql = SqlParserUtils.parseSQL(_boundSql.getSql(), tableAlias, ShiroUtils.getUserEntity()==null? 0:ShiroUtils.getUserEntity().getDeptId());
                        // 原始的SQL语句
                        // String sql = _boundSql.getSql();
                        // 此处省略一系列改造代码
                        // String pageSql = sql + " limit " + page.getDbIndex() + "," + page.getDbNumber();
                        metaObject.setValue("delegate.boundSql.sql", sql);
                    //}

                    BoundSql newBoundSql = new BoundSql(
                            mappedStatement.getConfiguration(),
                            SqlParserUtils.parseSQL(boundSql.getSql(), tableAlias, ShiroUtils.getUserEntity()==null? 0:ShiroUtils.getUserEntity().getDeptId()),
                            boundSql.getParameterMappings(),
                            boundSql.getParameterObject());

                    //invocation.getArgs()[0] = buildMappedStatement(mappedStatement,new BoundSqlSqlSource(newBoundSql));
                }
                break;
            }
        }
        return invocation.proceed();
    }



    /**
     * 根据已有MappedStatement构造新的MappedStatement
     * @param ms
     * @param sqlSource
     * @return
     */
    private MappedStatement buildMappedStatement(MappedStatement ms, SqlSource sqlSource) {
        MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), sqlSource, ms.getSqlCommandType());
        builder.resource(ms.getResource());
        builder.fetchSize(ms.getFetchSize());
        builder.statementType(ms.getStatementType());
        builder.keyGenerator(ms.getKeyGenerator());
        if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {
            StringBuilder keyProperties = new StringBuilder();
            for (String keyProperty : ms.getKeyProperties()) {
                keyProperties.append(keyProperty).append(",");
            }
            keyProperties.delete(keyProperties.length() - 1, keyProperties.length());
            builder.keyProperty(keyProperties.toString());
        }
        builder.timeout(ms.getTimeout());
        builder.parameterMap(ms.getParameterMap());
        builder.resultMaps(ms.getResultMaps());
        builder.resultSetType(ms.getResultSetType());
        builder.cache(ms.getCache());
        builder.flushCacheRequired(ms.isFlushCacheRequired());
        builder.useCache(ms.isUseCache());
        return builder.build();
    }



    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }



    /**
     * 获取配置信息
     * @param properties
     */
    @Override
    public void setProperties(Properties properties) {
        // do nothing
    }



    /**
     * 用于构造新MappedStatement
     */
    public static class BoundSqlSqlSource implements SqlSource {
        BoundSql boundSql;

        public BoundSqlSqlSource(BoundSql boundSql) {
            this.boundSql = boundSql;
        }

        public BoundSql getBoundSql(Object parameterObject) {
            return boundSql;
        }
    }
}
